package com.liuceng.zen.lib.gdt;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.github.kevinsawicki.http.HttpRequest;
import com.liuceng.zen.lib.gdt.util.GdtUtils;
import com.liuceng.zen.lib.gdt.vo.ApiDomain;
import com.liuceng.zen.lib.gdt.vo.GdtList;
import com.liuceng.zen.lib.gdt.vo.GdtResult;

@Service
public class GdtClient<T extends ApiDomain> {
  protected static final Logger LOG = LoggerFactory.getLogger(GdtClient.class);

  protected static final String CREATE = "create";
  protected static final String READ = "read";
  protected static final String UPDATE = "update";
  protected static final String DELETE = "delete";
  protected static final String SELECT = "select";
  protected static final String COUNT = "count";

  /**
   * gdt_appid=51957 gdt_key=c543f7c880841ef18d3dbdf59f5c058b gdt_sub_customer_id=51966
   * gdt_api_url=sandbox.api.e.qq.com
   */
  // @Value("#{sys.gdt_appid}")
  public String appid = "86992";

  // @Value("#{sys.gdt_key}")
  public String appkey = "1e4558ae3932d467a6a7255b914b8ca2";

  // @Value("#{sys.gdt_api_url}")
  public String apiUrl = "https://api.e.qq.com/luna/v2";

  public boolean validateResultCode(int code) {
    switch (code) {
      case GdtCode.CODE_正常:
        return true;
      case GdtCode.CODE_余额不足_划账:
      case GdtCode.CODE_订单号重复_充值:
        return false;
    }
    return false;
  }

  public GdtResult<T> read(String resName, TypeReference<GdtResult<T>> tp,
      Map<String, Object> params, List<String> fields) {
    String url = GdtUtils.buildUrl(apiUrl, resName, READ);
    // 把http://sandbox.api.e.qq.com/luna/v2拼接成http://sandbox.api.e.qq.com/luna/v2/campaign/read
    params.put("token", GdtUtils.buildToken(Integer.parseInt(appid), appid, appkey));
    if (fields != null) {
      params.put("fields", fields);
    }
    //
    HttpRequest req = HttpRequest.get(url, true, map2arr(params));
    if (200 != req.code()) {
      LOG.error("read error:" + req.code() + "\r\n body:" + req.body());
      return new GdtResult<T>(-1, "network error!");
    }

    String content = req.body();
    if (StringUtils.isNoneBlank(content) && !content.contains("\"ret\":0")) {
      LOG.error("read error:" + content);
      return new GdtResult<T>(-1, "network error!" + content);
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug(content);
    }
    GdtResult<T> res = JSON.parseObject(content, tp);
    return res;
  }

  public GdtResult<GdtList<T>> select(String resName, TypeReference<GdtResult<GdtList<T>>> tp,
      Map<String, Object> params, Map<String, Object> filter, Map<String, Object> where,
      List<String> fields, List<String> groupBy, Integer page, Integer pageSize) {
    if (filter != null) {
      params.put("filter", filter);
    }
    if (groupBy != null) {
      params.put("group_by", formatList(groupBy));
    }
    return select(resName, tp, params, where, fields, page, pageSize);
  }

  public GdtResult<GdtList<T>> select(String resName, TypeReference<GdtResult<GdtList<T>>> tp,
      Map<String, Object> params, Map<String, Object> filter, Map<String, Object> where,
      List<String> fields, Integer page, Integer pageSize) {
    if (filter != null) {
      params.put("filter", filter);
    }
    return select(resName, tp, params, where, fields, page, pageSize);
  }

  public GdtResult<GdtList<T>> select(String resName, TypeReference<GdtResult<GdtList<T>>> tp,
      Map<String, Object> params, Map<String, Object> where, List<String> fields, Integer page,
      Integer pageSize) {
    String url = GdtUtils.buildUrl(apiUrl, resName, SELECT);
    //
    params.put("token", GdtUtils.buildToken(Integer.parseInt(appid), appid, appkey));
    if (fields != null) {
      params.put("fields", fields);
    }
    if (where != null && !where.isEmpty()) {
      params.put("where", formatMap(where));
    }
    params.put("page", page);
    params.put("page_size", pageSize);
    HttpRequest req = HttpRequest.get(url, true, map2arr(params));
    if (200 != req.code()) {
      LOG.error("select error:" + req.code() + "\r\n body:" + req.body());
      return new GdtResult<GdtList<T>>(-1, "network error!");
    }

    String content = req.body();
    if (StringUtils.isNoneBlank(content) && !content.contains("\"ret\":0")) {
      LOG.error("read error:" + content);
      if (content.contains("\"ret\":4632")) {
        return new GdtResult<GdtList<T>>(4632, "reached the api limit " + content);
      }
      return new GdtResult<GdtList<T>>(-1, "network error!" + content);
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug(content);
    }
    GdtResult<GdtList<T>> res = null;
    try {
      res = JSON.parseObject(content, tp);
    } catch (Exception e) {
      LOG.error("json format exception: " + content);
      return new GdtResult<GdtList<T>>(-1, "network error!");
    }
    return res;
  }

  public GdtResult<T> create(T domain) {
    String url = GdtUtils.buildUrl(apiUrl, T.RES_NAME, CREATE);
    // HttpRequest req = HttpRequest.p
    return null;
  }

  public GdtResult<T> update(T domain) {
    String url = GdtUtils.buildUrl(apiUrl, T.RES_NAME, UPDATE);
    // TODO
    return null;
  }

  public GdtResult<T> delete(Map<String, Object> params) {
    String url = GdtUtils.buildUrl(apiUrl, T.RES_NAME, DELETE);
    // TODO
    return null;
  }

  // 获取公共信息
  public GdtResult<GdtList<T>> selectUtilities(String resName, String action,
      TypeReference<GdtResult<GdtList<T>>> typeReference) {
    String url = GdtUtils.buildUrl(apiUrl, resName, action);
    Map<String, Object> params = new HashMap<>();
    params.put("token", GdtUtils.buildToken(Integer.parseInt(appid), appid, appkey));
    HttpRequest req = HttpRequest.get(url, true, map2arr(params));
    if (200 != req.code()) {
      LOG.error("read error:" + req.code() + "\r\n body:" + req.body());
      return new GdtResult<GdtList<T>>(-1, "network error!");
    }

    String content = req.body();
    if (StringUtils.isNoneBlank(content) && !content.contains("\"ret\":0")) {
      LOG.error("read error:" + content);
    }
    if (LOG.isDebugEnabled()) {
      LOG.debug(content);
    }
    GdtResult<GdtList<T>> res = JSON.parseObject(content, typeReference);

    return res;
  }

  protected Object[] map2arr(Map<String, Object> params) {
    if (params == null || params.isEmpty()) {
      return null;
    }
    Object[] keys = params.keySet().toArray();
    Object[] vals = params.values().toArray();
    List<Object> objList = new ArrayList<>();
    for (int i = 0; i < keys.length; i++) {
      Object key = keys[i];
      Object val = vals[i];
      if (val != null) {
        objList.add(key);
        objList.add(val);
      }
    }
    return objList.toArray();
  }

  public String formatList(List<String> list) {
    StringBuilder sb = new StringBuilder();
    List<String> newList = list.stream().map(elm -> {
      return "\"" + elm + "\"";
    }).collect(Collectors.toList());
    sb.append("[").append(StringUtils.join(newList, ",")).append("]");
    return sb.toString();
  }

  public String formatMap(Map<String, Object> params) {
    Object[] keys = params.keySet().toArray();
    List<String> str = new ArrayList<>();
    for (Object keyObj : keys) {
      String key = (String) keyObj;
      String segment =
          StringUtils.join(new String[] {"\"", key, "\":\"", params.get(key).toString(), "\""});
      str.add(segment);
    }
    StringBuilder sb = new StringBuilder();
    sb.append("{");
    sb.append(StringUtils.join(str.toArray(new String[str.size()]), ","));
    sb.append("}");
    return sb.toString();
  }
}
